package frontend.reports;

import frontend.core.TimeTrackingWebApplication;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import shared.util.CalendarUtils;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.util.IOUtils;
import org.apache.wicket.Localizer;

import javax.servlet.ServletContext;
import java.io.*;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class XLSFileCreator {

    private static Logger logger = Logger.getLogger(XLSFileCreator.class);

    private static final String SHEET_NAME = "report.sheet.name.reportResults";
    private Workbook workbook;
    private Sheet dataSheet;
    CellStyle headerCellStyle;
    CellStyle rowCellStyle;
    private int rowIndex = 0;
    private int numberOfColumns = 0;

    public XLSFileCreator(Map<String, String> details, List<String> header, List<Object[]> rows) {
        workbook = new XSSFWorkbook();
        dataSheet = workbook.createSheet(Localizer.get().getString(SHEET_NAME, null));
        headerCellStyle = createHeaderStyle();
        rowCellStyle = createRowStyle();

        createDetails(details);
        createHeader(header);
        createRows(rows);
        setAutoSizeForAllColumns(header);
        addLogo();
    }

    private void createDetails(Map<String, String> details) {
        CellStyle detailNameCellStyle = createDetailsNameStyle();
        CellStyle detailValueCellStyle = createDetailsValueStyle();
        for (String detailName : details.keySet()) {
            String detailValue = details.get(detailName);
            Row detailRow = dataSheet.createRow(rowIndex++);
            detailRow.setHeight((short) 0x150);

            Cell detailNameCell = detailRow.createCell((short) 0);
            detailNameCell.setCellValue(new XSSFRichTextString(detailName));
            detailNameCell.setCellStyle(detailNameCellStyle);

            Cell detailValueCell = detailRow.createCell((short) 1);
            detailValueCell.setCellValue(new XSSFRichTextString(detailValue));
            detailValueCell.setCellStyle(detailValueCellStyle);

            // Small hack, to skip one row after first 3 common detail rows (report name, run by & run at)
            if (rowIndex == 3) {
                rowIndex++;
            }
        }
        rowIndex = rowIndex + 2;
    }

    private CellStyle createDetailsNameStyle() {
        Font font = workbook.createFont();
        font.setFontName(HSSFFont.FONT_ARIAL);
        font.setColor(HSSFColor.BLACK.index);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFont(font);
        cellStyle.setAlignment(HSSFCellStyle.ALIGN_LEFT);
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        return cellStyle;
    }

    private CellStyle createDetailsValueStyle() {
        Font font = workbook.createFont();
        font.setFontName(HSSFFont.FONT_ARIAL);
        font.setColor(HSSFColor.BLACK.index);
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFont(font);
        cellStyle.setAlignment(HSSFCellStyle.ALIGN_LEFT);
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        return cellStyle;
    }

    private void createHeader(List<String> header) {
        numberOfColumns = header.size();
        Row headerRow = dataSheet.createRow(rowIndex++);
        headerRow.setHeight((short) 0x200);


        for (short index = 0; index < header.size(); index++) {
            Cell cell = headerRow.createCell(index);
            cell.setCellStyle(headerCellStyle);
            cell.setCellValue(new XSSFRichTextString(header.get(index)));
        }
    }

    private CellStyle createHeaderStyle() {
        Font font = workbook.createFont();
        font.setFontName(HSSFFont.FONT_ARIAL);
        font.setColor(HSSFColor.BLACK.index);
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        font.setFontHeightInPoints((short) 12);
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFont(font);
        cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        cellStyle.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index);
        cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
        cellStyle.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);
        return cellStyle;
    }

    private void createRows(List<Object[]> rows) {
        for (Object[] row : rows) {
            Row xssfRow = dataSheet.createRow(rowIndex++);
            xssfRow.setHeight((short) 0x150);
            for (short index = 0; index < row.length; index++) {
                Cell cell = xssfRow.createCell(index);
                cell.setCellStyle(rowCellStyle);
                Object cellValue = row[index];
                addCellValue(cellValue, cell, rowCellStyle);
            }
        }
    }

    private void addCellValue(Object cellValue, Cell cell, CellStyle cellStyle) {
        if (cellValue == null) {
            cell.setCellValue(new XSSFRichTextString(""));
            cell.setCellType(Cell.CELL_TYPE_STRING);
        } else if (cellValue instanceof String) {
            String stringValue = (String) cellValue;
            cell.setCellValue(new XSSFRichTextString(stringValue));
            cell.setCellType(Cell.CELL_TYPE_STRING);
        } else if (cellValue instanceof Date) {
            Date dateValue = (Date) cellValue;
            cell.setCellValue(new XSSFRichTextString(CalendarUtils.formatDate(dateValue)));
            cell.setCellType(Cell.CELL_TYPE_STRING);
        } else if (cellValue instanceof Double) {
            double doubleValue = (Double) cellValue;
            cell.setCellValue(doubleValue);
            cell.setCellType(Cell.CELL_TYPE_NUMERIC);
            cellStyle.setDataFormat(workbook.createDataFormat().getFormat("0"));
        } else if (cellValue instanceof Boolean) {
            Boolean booleanValue = (Boolean) cellValue;
            cell.setCellValue(booleanValue);
            cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
        } else {
            throw new RuntimeException("Unsupported data type included in the report.");
        }
    }

    private CellStyle createRowStyle() {
        Font font = workbook.createFont();
        font.setFontName(HSSFFont.FONT_ARIAL);
        font.setColor(HSSFFont.COLOR_NORMAL);
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFont(font);
        cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        cellStyle.setBorderBottom(HSSFCellStyle.BORDER_DOTTED);
        cellStyle.setBorderLeft(HSSFCellStyle.BORDER_DOTTED);
        cellStyle.setBorderRight(HSSFCellStyle.BORDER_DOTTED);
        return cellStyle;
    }

    private void setAutoSizeForAllColumns(List<String> header) {
        for (short index = 0; index < header.size(); index++) {
            dataSheet.autoSizeColumn(index);
        }
    }

    private void addLogo() {
        int pictureIdx = 0;
        try {
            ServletContext servletContext = TimeTrackingWebApplication.get().getServletContext();
            InputStream is = servletContext.getResourceAsStream("/images/logo-black.png");
            byte[] bytes = IOUtils.toByteArray(is);
            pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
            is.close();
        } catch (FileNotFoundException e) {
            logger.error("Error while inserting the logo into the report!", e);
            return;
        } catch (IOException e) {
            logger.error("Error while inserting the logo into the report!", e);
            return;
        }

        CreationHelper helper = workbook.getCreationHelper();
        Drawing drawing = dataSheet.createDrawingPatriarch();
        ClientAnchor anchor = helper.createClientAnchor();
        anchor.setCol1(numberOfColumns-3);
        Picture pict = drawing.createPicture(anchor, pictureIdx);
        pict.resize(0.3);
    }

    public File getXLSFile() {
        FileOutputStream fileOut = null;
        File file = new File("workbook.xlsx");
        try {
            fileOut = new FileOutputStream(file);
            workbook.write(fileOut);
            fileOut.close();
        } catch (IOException e) {
            logger.error("Error while creating the report XLSX file!");
            e.printStackTrace();
        }
        return file;
    }
}
